/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package dom.workflow;

import dom.Command;
import dom.Reciever;
import imp.reciever.output.OutputReciever;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

/**
 *
 * @author julian.pena
 */
public class WorkFlow implements Command{
    private String name;
    private List<Command> commands;
    private RunMode runMode;
    private int mainCommand;
    private WorkFlow father;

    public WorkFlow(InputStream inputStream, OutputStream outputStream, List<Command> commands, RunMode runMode, int mainCommand,String name) throws Exception {
        this.father = null;
        this.runMode = runMode;
        this.name = name;
        if (runMode == RunMode.SEQUENTIAL) {
            this.mainCommand = -1;
            this.commands = commands;
            commands.get(0).setInputStream(inputStream);
            commands.get(commands.size() - 1).setOutputStream(outputStream);
            for (int i = 0; i < commands.size() -1; i++) {
                Command c1 = commands.get(i);
                Command c2 = commands.get(i + 1);
                c1.setFather(this);
                c2.setFather(this);
                try {
                    WorkFlowHelper.connectCommands(c1, c2);
                } catch (Exception ex) {
                    throw new Exception((i+1) + " y " + (i+2) + ": " + ex.getMessage());
                }
            }
        } else {
            if (mainCommand < 0 && mainCommand >= commands.size()) {
                throw new Exception("invalid index");
            }
            this.mainCommand = mainCommand;
            commands.get(mainCommand).setInputStream(inputStream);
            commands.get(mainCommand).setOutputStream(outputStream);
        }
        
    }

    public String readyToRun(){
        boolean inputReady = this.isInputReady();
        boolean outputReady = this.isOutputReady();
        String message = null;
        if (inputReady && outputReady) {
            message = "Listo";
        } else if (!inputReady && !outputReady) {
            message = "Necesita un flujo de entrada y un flujo de salida";
        } else if(!inputReady){
            message = "Necesita un flujo de entrada";
        } else if(!outputReady){
            message = "Necesita un flujo de salida";
        }
        return message;
    }
    
    @Override
    public void execute() {
        Thread[] threads =  new Thread[commands.size()];
        int i = 0;
        if (runMode == RunMode.SEQUENTIAL) {
            for (Command command : commands) {
                threads[i] = new Thread(command);
                threads[i].start();
                if (command.getReciever()instanceof OutputReciever) {
                    try {
                        threads[i].join();
                    } catch (InterruptedException ex) {}
                }
                i++;
            }
        } else {
            for (Command command : commands) {
                threads[i] = new Thread(command);
                threads[i].start();
                i++;
            }
            for (Thread thread : threads) {
                try {
                    thread.join();
                } catch (InterruptedException ex) {}
            }
        }
        
    }

    @Override
    public InputStream getInputStream() {
        if (runMode == RunMode.SEQUENTIAL) {
            return commands.get(0).getInputStream();
        } else {
            return commands.get(mainCommand).getInputStream();
        }
    }

    @Override
    public void setInputStream(InputStream inputStream) {
        if (runMode == RunMode.SEQUENTIAL) {
            commands.get(0).setInputStream(inputStream);
        } else {
            commands.get(mainCommand).setInputStream(inputStream);
        }
    }

    @Override
    public OutputStream getOutputStream() {
        if (runMode == RunMode.SEQUENTIAL) {
            return commands.get(commands.size() -1 ).getOutputStream();
        } else {
            return commands.get(mainCommand).getOutputStream();
        }
    }

    @Override
    public void setOutputStream(OutputStream outputStream) {
        if (runMode == RunMode.SEQUENTIAL) {
            commands.get(commands.size() - 1).setOutputStream(outputStream);
        } else {
            commands.get(mainCommand).setOutputStream(outputStream);
        }
    }

    @Override
    public void run() {
        execute();
    }

    @Override
    public void setFather(WorkFlow workflow) {
        this.father = workflow;
    }

    public void notifyStop(Command command,String fileName) {
        if (runMode == RunMode.SEQUENTIAL) {
            int index = commands.indexOf(command);
            if (index != commands.size() -1) {
                commands.get(index + 1).stop(fileName);
            } else {
                if (father != null) {
                    notifyStop(fileName);
                } else {
                    // Como es el ultimo paso, no espera
                }
            }
        } else {
            if (father != null) {
                father.notifyStop(this, fileName);
            } else {
                // Como es el ultimo paso, no espera
            }
        }
    }

    @Override
    public void notifyStop(String fileName) {
        father.notifyStop(this, fileName);
    }

    @Override
    public void stop(String fileName) {
        if (runMode == RunMode.SEQUENTIAL) {
            commands.get(0).stop(fileName);
        } else {
            commands.get(mainCommand).stop(fileName);
        }
    }

    @Override
    public boolean isInputReady() {
        if (runMode == RunMode.SEQUENTIAL) {
            return commands.get(0).isInputReady();
        } else {
            for (Command command : commands) {
                if (!command.isInputReady()) {
                    return false;
                }
            }
            return true;
        }
    }

    @Override
    public boolean isOutputReady() {
        if (runMode == RunMode.SEQUENTIAL) {
            return commands.get(commands.size() - 1).isOutputReady();
        } else {
            for (Command command : commands) {
                if (!command.isOutputReady()) {
                    return false;
                }
            }
            return true;
        }
    }

    public void notifyError(Command command) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void notifyError() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public String toString() {
        return name;
    }

    @Override
    public Reciever getReciever() {
        return null;
    }
}
